package com.ncmmall.application.business.order.wechat;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.feijin.commons.lang.PropertiesHelps;
import com.github.wxpay.sdk.WXPayUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author linxs
 * 微信退款请求
 */
@Slf4j
public class WechatRefundRequest {

    private String outTradeNo;
    private double payAmount;
    private double refundAmount;
    private String outRefundNo;

    private static final String WX_KEY = PropertiesHelps.getValue("wx.key");
    private static final String APPID = PropertiesHelps.getValue("wx.appid");
    private static final String MCH_ID = PropertiesHelps.getValue("wx.mch_id");
    private static final String SUB_MCH_ID = PropertiesHelps.getValue("wx.sub_mch_id");
    private static final String SUB_APPID = PropertiesHelps.getValue("wx.sub_appid");
    private static final String REFUND_PATH = PropertiesHelps.getValue("WX.MP.REFUND.PATH");
    private static final String REFUND_URL = PropertiesHelps.getValue("WX.MP.REFUND.URL");

    private static final Object lock = new Object();
    private static HttpClient httpClientWithKey;

    public WechatRefundRequest() {
    }

    public WechatRefundRequest(String outTradeNo, double payAmount, double refundAmount, String outRefundNo) {
        this.outTradeNo = outTradeNo;
        this.payAmount = payAmount;
        this.refundAmount = refundAmount;
        this.outRefundNo = outRefundNo;
    }

    public Map<String, String> refundByWechatMp() throws RuntimeException {
        return WechatMp();
    }

    private Map<String, String> WechatMp() throws RuntimeException {
        Map<String, String> data = new HashMap<>();

        try {

            Map<String, String> reqData = new HashMap<>();
            reqData.put("appid", APPID);
            reqData.put("mch_id", MCH_ID);
            reqData.put("sub_appid", SUB_APPID);
            reqData.put("sub_mch_id", SUB_MCH_ID);
            reqData.put("nonce_str", RandomUtil.randomString(32));
            reqData.put("out_trade_no", outTradeNo);

            //商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔
            reqData.put("out_refund_no", outRefundNo);
            reqData.put("total_fee", String.valueOf((int) NumberUtil.mul(payAmount, 100)));
            reqData.put("refund_fee", String.valueOf((int) NumberUtil.mul(refundAmount, 100)));
            reqData.put("sign", buildSign(reqData));

            String xml = XmlUtil.mapToXmlStr(reqData);
            log.info("[微信退款请求xml:]" + xml);

            HttpClient httpClient = getHttpClient();

            HttpPost httpost = new HttpPost(REFUND_URL);
            httpost.setEntity(new StringEntity(xml, "UTF-8"));
            HttpResponse weixinResponse = httpClient.execute(httpost);
            String entity = EntityUtils.toString(weixinResponse.getEntity(), "UTF-8");

            Map<String, String> map = WechatRefundUtil.xmlToMap(entity);
            log.info("[微信退款返回参数:]" + map);

            if ("FAIL".equals(map.get("result_code"))) {
                log.error("[微信退款请求失败,错误原因]" + map.get("err_code_des"));
                data.put("flag", "false");
                data.put("message", map.get("err_code_des"));
                return data;
            }

            if ("SUCCESS".equals(map.get("return_code")) && "SUCCESS".equals(map.get("result_code"))) {
                log.info("退款请求成功");
                data.put("flag", "true");
                data.put("no", map.get("refund_id"));
            } else {
                log.error("[微信退款提交业务失败,错误原因]" + map.get("err_code_des"));
                data.put("flag", "false");
                data.put("message", map.get("err_code_des"));
            }

        } catch (Exception e) {
            log.error("[退款异常]", e);
            data.put("flag", "false");
        }
        return data;
    }

    private HttpClient getHttpClient() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException {
        if (httpClientWithKey == null) {
            synchronized (lock) {
                if (httpClientWithKey == null) {
                    log.info("mch_id: {}", MCH_ID);
                    KeyStore keyStore = KeyStore.getInstance("PKCS12");
                    try (InputStream instream = new FileInputStream(REFUND_PATH)) {
                        keyStore.load(instream, MCH_ID.toCharArray());
                    }
                    SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, MCH_ID.toCharArray()).build();
                    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"},
                            null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
                    httpClientWithKey = HttpClients.custom().setSSLSocketFactory(sslsf).build();
                }
            }
        }
        return httpClientWithKey;
    }


    /**
     * 企业付款
     */
    public Map<String, String> mmpaymkttransfers(Map<String, String> reqData) throws RuntimeException {

        Map<String, String> data = new HashMap<>();

        try {
            log.info("[微信企业付款请求开始:]");

            reqData.put("mchid", MCH_ID);
            reqData.put("mch_appid", APPID);
            reqData.put("nonce_str", WXPayUtil.generateNonceStr());
//            reqData.put("sign", buildSign(reqData));
            reqData.put("sign", WXPayUtil.generateSignature(reqData, WX_KEY));
            log.info("[请求参数:]" + reqData);

            String xml = WechatRefundUtil.mapToXml(reqData);
            log.info("[请求xml:]" + xml);

            HttpClient httpClient = getHttpClient();

            HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers");
            httpost.setEntity(new StringEntity(xml, "UTF-8"));
            HttpResponse weixinResponse = httpClient.execute(httpost);

            String xmlStr = EntityUtils.toString(weixinResponse.getEntity(), "UTF-8");
            log.info("[返回xml:]" + xmlStr);

            Map<String, String> map = WechatRefundUtil.xmlToMap(xmlStr);
            log.info("[返回参数:]" + map);


            if ("FAIL".equals(map.get("return_code"))) {
                log.error("提现支付请求失败,错误原因:" + map.get("return_msg"));
                data.put("flag", "false");
                data.put("message", map.get("return_msg"));
                return data;
            }

            if ("SUCCESS".equals(map.get("return_code")) && "SUCCESS".equals(map.get("result_code"))) {
                log.info("提现请求成功");
                data.put("flag", "true");
                data.put("payment_no", map.get("payment_no"));
            } else {
                log.error("提交业务失败,错误原因:" + map.get("err_code_des"));
                data.put("flag", "false");
                data.put("message", map.get("err_code_des"));
            }
        } catch (Exception e) {
            log.error("[提现异常]", e);
            data.put("flag", "false");
            data.put("message", "支付异常");
        }

        return data;
    }

    private String buildSign(Map<String, String> data) {
        String str = MapUtil.sortJoin(data, "&", "=", true, "&key=" + WX_KEY);
        return DigestUtil.md5Hex(str).toUpperCase();
    }

}
